import ctypes
import os
import time
import sys

class IdCardReader:
    """身份证读卡器集成类"""
    
    def __init__(self):
        self.ter_dll = None
        self.connected = False
        self.port = None
        
    def load_dll(self):
        """加载DLL文件"""
        print("=== 加载身份证读卡DLL ===")
        
        # 所有可能的DLL路径
        possible_paths = [
            "Termb.dll",  # 当前目录
            os.path.join(os.path.dirname(os.path.abspath(__file__)), "Termb.dll"),  # 脚本目录
            os.path.join(os.path.dirname(os.path.abspath(__file__)), "CVR100U", "Termb.dll"),  # 子目录
            "C:\\Termb.dll",  # C盘根目录
            os.path.join(os.environ.get('WINDIR', 'C:\\Windows'), "System32", "Termb.dll")  # 系统目录
        ]
        
        # 尝试加载DLL
        for path in possible_paths:
            if os.path.exists(path):
                try:
                    print(f"尝试加载: {path}")
                    self.ter_dll = ctypes.windll.LoadLibrary(path)
                    print(f"成功加载DLL: {path}")
                    
                    # 检查相关文件
                    dll_dir = os.path.dirname(path)
                    self.check_related_files(dll_dir)
                    
                    return True
                except Exception as e:
                    print(f"加载 {path} 失败: {str(e)}")
        
        print("错误: 无法加载Termb.dll")
        return False
    
    def check_related_files(self, dll_dir):
        """检查相关文件"""
        print("\n=== 检查相关文件 ===")
        related_files = ["sdtapi.dll", "WltRS.dll", "DLL_File.dll"]
        for file in related_files:
            path = os.path.join(dll_dir, file)
            if os.path.exists(path):
                print(f"找到 {file}: {path}")
            else:
                print(f"警告: 未找到 {file}")
        
        # 检查授权文件
        license_path = "C:\\termb.lic"
        if os.path.exists(license_path):
            print(f"找到授权文件: {license_path}")
        else:
            print(f"警告: 未找到授权文件 {license_path}")
            
            # 尝试从项目目录复制
            src_license = os.path.join(dll_dir, "license.dat")
            if os.path.exists(src_license):
                try:
                    import shutil
                    shutil.copy(src_license, license_path)
                    print(f"已复制授权文件 {src_license} 到 {license_path}")
                except Exception as e:
                    print(f"无法复制授权文件: {str(e)}")
    
    def connect(self):
        """尝试连接设备"""
        if not self.ter_dll:
            print("错误: DLL未加载，无法连接")
            return False
            
        print("\n=== 尝试连接设备 ===")
        
        # 尝试多个端口，优先尝试1001
        priority_ports = [1001] + [p for p in range(1002, 1017)]
        for port in priority_ports:
            try:
                print(f"尝试端口 {port}... ", end="")
                result = self.ter_dll.CVR_InitComm(port)
                if result == 1:
                    self.connected = True
                    self.port = port
                    print("成功!")
                    return True
                else:
                    print(f"失败 (错误码: {result})")
            except Exception as e:
                print(f"异常: {str(e)}")
        
        print("错误: 无法连接到设备，请检查设备连接和驱动")
        return False
    
    def disconnect(self):
        """断开连接"""
        if self.connected and self.ter_dll:
            self.ter_dll.CVR_CloseComm()
            self.connected = False
            print("已断开设备连接")
            return True
        return False
    
    def authenticate_card(self):
        """认证卡片"""
        if not self.connected:
            print("错误: 未连接设备，请先连接")
            return False
            
        print("\n=== 尝试认证卡片 ===")
        print("请将身份证放在读卡区...")
        time.sleep(1)  # 稍微等待用户放卡
        
        # 先尝试标准认证
        try:
            print("尝试标准认证... ", end="")
            auth_result = self.ter_dll.CVR_Authenticate()
            if auth_result == 1:
                print("成功!")
                return True
            else:
                print(f"失败 (错误码: {auth_result})")
        except Exception as e:
            print(f"发生异常: {str(e)}")
        
        # 标准认证失败，尝试无判断认证
        try:
            print("尝试无判断认证... ", end="")
            time.sleep(0.5)  # 增加延时
            auth_result = self.ter_dll.CVR_AuthenticateForNoJudge()
            if auth_result == 1:
                print("成功!")
                return True
            else:
                print(f"失败 (错误码: {auth_result})")
        except Exception as e:
            print(f"发生异常: {str(e)}")
        
        print("认证失败，请确保身份证放置正确")
        return False
    
    def read_card_content(self):
        """读取卡内容"""
        if not self.connected:
            print("错误: 未连接设备，请先连接")
            return False
            
        print("\n=== 尝试读取身份证内容 ===")
        
        # 先尝试CVR_Read_Content方法
        try:
            print("尝试CVR_Read_Content... ", end="")
            read_result = self.ter_dll.CVR_Read_Content(1)
            if read_result == 1:
                print("成功!")
                return True
            else:
                print(f"失败 (错误码: {read_result})")
        except Exception as e:
            print(f"读取内容异常: {str(e)}")
        
        # 尝试CVR_Read_FPContent方法
        try:
            print("尝试CVR_Read_FPContent... ", end="")
            read_result = self.ter_dll.CVR_Read_FPContent()
            if read_result == 1:
                print("成功!")
                return True
            else:
                print(f"失败 (错误码: {read_result})")
        except Exception as e:
            print(f"发生异常: {str(e)}")
        
        print("读取内容失败")
        return False
    
    def get_id_card_data(self):
        """直接从内存获取身份证信息"""
        if not self.ter_dll:
            return None
        
        try:
            # 创建缓冲区和长度变量
            buffer_size = 128
            info = {}
            
            # 获取姓名
            try:
                name_buffer = ctypes.create_string_buffer(buffer_size)
                name_len = ctypes.c_int(buffer_size)
                if self.ter_dll.GetPeopleName(name_buffer, ctypes.byref(name_len)) == 1:
                    info["姓名"] = name_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取姓名失败: {e}")
            
            # 获取性别
            try:
                sex_buffer = ctypes.create_string_buffer(buffer_size)
                sex_len = ctypes.c_int(buffer_size)
                if self.ter_dll.GetPeopleSex(sex_buffer, ctypes.byref(sex_len)) == 1:
                    info["性别"] = sex_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取性别失败: {e}")
            
            # 获取身份证号
            try:
                id_buffer = ctypes.create_string_buffer(buffer_size)
                id_len = ctypes.c_int(buffer_size)
                if self.ter_dll.GetPeopleIDCode(id_buffer, ctypes.byref(id_len)) == 1:
                    info["身份证号"] = id_buffer.value.decode('gbk', errors='ignore')
            except Exception as e:
                print(f"获取身份证号失败: {e}")
            
            # 获取出生日期并计算年龄
            try:
                birthday_buffer = ctypes.create_string_buffer(buffer_size)
                birthday_len = ctypes.c_int(buffer_size)
                if self.ter_dll.GetPeopleBirthday(birthday_buffer, ctypes.byref(birthday_len)) == 1:
                    birthday = birthday_buffer.value.decode('gbk', errors='ignore')
                    if len(birthday) == 8:  # 确保生日格式正确 (YYYYMMDD)
                        from datetime import datetime
                        birth_date = datetime.strptime(birthday, "%Y%m%d")
                        today = datetime.today()
                        age = today.year - birth_date.year
                        
                        # 如果今年的生日还没过，年龄减1
                        if (today.month, today.day) < (birth_date.month, birth_date.day):
                            age -= 1
                        
                        info["年龄"] = f"{age}岁"
            except Exception as e:
                print(f"计算年龄失败: {e}")
            
            return info
            
        except Exception as e:
            print(f"获取身份证信息失败: {str(e)}")
            return None
    
    def find_photo_file(self):
        """查找身份证照片文件"""
        possible_img_names = ["zp.bmp", "photo.bmp", "pic.bmp", "zp.jpg", "zp.jpeg"]
        
        # 搜索目录
        search_dirs = [
            os.getcwd(),                                  # 当前目录
            os.path.dirname(os.path.abspath(__file__)),   # 脚本目录
            "C:\\",                                       # C盘根目录
            os.environ.get('TEMP', 'C:\\Temp'),           # 临时目录
            os.path.join(os.environ.get('LOCALAPPDATA', ''), "Temp", "chinaidcard"),  # 官方文档提到的默认路径
        ]
        
        # 查找图片文件
        for directory in search_dirs:
            if os.path.exists(directory):
                for filename in possible_img_names:
                    path = os.path.join(directory, filename)
                    if os.path.exists(path):
                        return path
        
        return None
    
    def read_card(self):
        """完整的读卡流程，返回身份证信息字典"""
        if not self.ter_dll and not self.load_dll():
            return None
            
        try:
            # 连接设备
            if not self.connect():
                return None
                
            # 认证卡片
            if not self.authenticate_card():
                self.disconnect()
                return None
                
            # 读取内容
            if not self.read_card_content():
                self.disconnect()
                return None
                
            # 获取数据
            info = self.get_id_card_data()
            
            # 查找照片文件
            photo_path = self.find_photo_file()
            if photo_path:
                info["照片路径"] = photo_path
            
            # 断开连接
            self.disconnect()
            
            return info
            
        except Exception as e:
            print(f"\n读卡过程发生异常: {str(e)}")
            if self.connected:
                self.disconnect()
            return None 